{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "8a00fd68-aa55-424d-b54f-7ab3bc7fafc3",
   "metadata": {},
   "source": [
    "# 使用私有数据训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "0e776ac5-bf5b-4596-8a2f-c03a39989f55",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "PyTorch built with:\n",
      "  - GCC 9.3\n",
      "  - C++ Version: 201703\n",
      "  - Intel(R) oneAPI Math Kernel Library Version 2022.2-Product Build 20220804 for Intel(R) 64 architecture applications\n",
      "  - Intel(R) MKL-DNN v3.3.6 (Git Hash 86e6af5974177e513fd3fee58425e1063e7f1361)\n",
      "  - OpenMP 201511 (a.k.a. OpenMP 4.5)\n",
      "  - LAPACK is enabled (usually provided by MKL)\n",
      "  - NNPACK is enabled\n",
      "  - CPU capability usage: AVX512\n",
      "  - CUDA Runtime 12.1\n",
      "  - NVCC architecture flags: -gencode;arch=compute_50,code=sm_50;-gencode;arch=compute_60,code=sm_60;-gencode;arch=compute_70,code=sm_70;-gencode;arch=compute_75,code=sm_75;-gencode;arch=compute_80,code=sm_80;-gencode;arch=compute_86,code=sm_86;-gencode;arch=compute_90,code=sm_90\n",
      "  - CuDNN 8.9.2\n",
      "  - Magma 2.6.1\n",
      "  - Build settings: BLAS_INFO=mkl, BUILD_TYPE=Release, CUDA_VERSION=12.1, CUDNN_VERSION=8.9.2, CXX_COMPILER=/opt/rh/devtoolset-9/root/usr/bin/c++, CXX_FLAGS= -D_GLIBCXX_USE_CXX11_ABI=0 -fabi-version=11 -fvisibility-inlines-hidden -DUSE_PTHREADPOOL -DNDEBUG -DUSE_KINETO -DLIBKINETO_NOROCTRACER -DUSE_FBGEMM -DUSE_QNNPACK -DUSE_PYTORCH_QNNPACK -DUSE_XNNPACK -DSYMBOLICATE_MOBILE_DEBUG_HANDLE -O2 -fPIC -Wall -Wextra -Werror=return-type -Werror=non-virtual-dtor -Werror=bool-operation -Wnarrowing -Wno-missing-field-initializers -Wno-type-limits -Wno-array-bounds -Wno-unknown-pragmas -Wno-unused-parameter -Wno-unused-function -Wno-unused-result -Wno-strict-overflow -Wno-strict-aliasing -Wno-stringop-overflow -Wsuggest-override -Wno-psabi -Wno-error=pedantic -Wno-error=old-style-cast -Wno-missing-braces -fdiagnostics-color=always -faligned-new -Wno-unused-but-set-variable -Wno-maybe-uninitialized -fno-math-errno -fno-trapping-math -Werror=format -Wno-stringop-overflow, LAPACK_INFO=mkl, PERF_WITH_AVX=1, PERF_WITH_AVX2=1, PERF_WITH_AVX512=1, TORCH_VERSION=2.3.1, USE_CUDA=ON, USE_CUDNN=ON, USE_CUSPARSELT=1, USE_EXCEPTION_PTR=1, USE_GFLAGS=OFF, USE_GLOG=OFF, USE_GLOO=ON, USE_MKL=ON, USE_MKLDNN=ON, USE_MPI=OFF, USE_NCCL=1, USE_NNPACK=ON, USE_OPENMP=ON, USE_ROCM=OFF, USE_ROCM_KERNEL_ASSERT=OFF, \n",
      " _CudaDeviceProperties(name='Tesla T4', major=7, minor=5, total_memory=16112MB, multi_processor_count=40)\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "\n",
    "print(torch.__config__.show(), torch.cuda.get_device_properties(0))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b9cc547f-cbae-4be8-896e-309f8548e60d",
   "metadata": {},
   "source": [
    "# 定义全局参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "b21e831e-47c4-494f-ad13-1c9129d231bc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义全局变量和参数\n",
    "model_name_or_path = 'THUDM/chatglm3-6b'  # 模型ID或本地路径\n",
    "# train_data_path =     # 训练数据路径\n",
    "#train_data_path = 'data/zhouyi_dataset_20240118_163659.csv'    # 训练数据路径(批量生成数据集）\n",
    "#训练数据集合\n",
    "train_data_pathes=['data/zhouyi_dataset_handmade.csv'\n",
    "                  ,'data/zhouyi_dataset_20240118_152413.csv'\n",
    "                  ,'data/zhouyi_dataset_20240118_163659.csv']\n",
    "eval_data_path = None                     # 验证数据路径，如果没有则设置为None\n",
    "seed = 8                                 # 随机种子\n",
    "max_input_length = 512                    # 输入的最大长度\n",
    "max_output_length = 1536                  # 输出的最大长度\n",
    "lora_rank = 16                             # LoRA秩\n",
    "lora_alpha = 32                           # LoRA alpha值\n",
    "lora_dropout = 0.05                       # LoRA Dropout率\n",
    "prompt_text = ''                          # 所有数据前的指令文本"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20d417c6-af0d-46a2-b05e-b3d5737a38cd",
   "metadata": {},
   "source": [
    "# 导入需要使用的组件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "3247755f-0641-426b-bc0a-1cc0cdf0dcc3",
   "metadata": {},
   "outputs": [],
   "source": [
    "from datasets import load_dataset\n",
    "from datasets import ClassLabel, Sequence\n",
    "import random\n",
    "import pandas as pd\n",
    "from IPython.display import display, HTML\n",
    "from transformers import AutoTokenizer\n",
    "import torch\n",
    "from typing import List, Dict, Optional\n",
    "from transformers import AutoModel, BitsAndBytesConfig\n",
    "from peft import TaskType, LoraConfig, get_peft_model, prepare_model_for_kbit_training\n",
    "from peft.utils import TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING\n",
    "import datetime\n",
    "from transformers import TrainingArguments, Trainer"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0a8cfe6e-1faa-4282-902f-6f54a79a54e7",
   "metadata": {},
   "source": [
    "# 定义需要的函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "22b1c4ba-b4f5-408f-b9f0-01828f4d453e",
   "metadata": {},
   "outputs": [],
   "source": [
    "def show_random_elements(dataset, num_examples=10):\n",
    "    assert num_examples <= len(dataset), \"Can't pick more elements than there are in the dataset.\"\n",
    "    picks = []\n",
    "    for _ in range(num_examples):\n",
    "        pick = random.randint(0, len(dataset)-1)\n",
    "        while pick in picks:\n",
    "            pick = random.randint(0, len(dataset)-1)\n",
    "        picks.append(pick)\n",
    "    \n",
    "    df = pd.DataFrame(dataset[picks])\n",
    "    for column, typ in dataset.features.items():\n",
    "        if isinstance(typ, ClassLabel):\n",
    "            df[column] = df[column].transform(lambda i: typ.names[i])\n",
    "        elif isinstance(typ, Sequence) and isinstance(typ.feature, ClassLabel):\n",
    "            df[column] = df[column].transform(lambda x: [typ.feature.names[i] for i in x])\n",
    "    display(HTML(df.to_html()))\n",
    "\n",
    "# tokenize_func 函数\n",
    "def tokenize_func(example, tokenizer, ignore_label_id=-100):\n",
    "    \"\"\"\n",
    "    对单个数据样本进行tokenize处理。\n",
    "\n",
    "    参数:\n",
    "    example (dict): 包含'content'和'summary'键的字典，代表训练数据的一个样本。\n",
    "    tokenizer (transformers.PreTrainedTokenizer): 用于tokenize文本的tokenizer。\n",
    "    ignore_label_id (int, optional): 在label中用于填充的忽略ID，默认为-100。\n",
    "\n",
    "    返回:\n",
    "    dict: 包含'tokenized_input_ids'和'labels'的字典，用于模型训练。\n",
    "    \"\"\"\n",
    "\n",
    "    # 构建问题文本\n",
    "    question = prompt_text + example['content']\n",
    "    if example.get('input', None) and example['input'].strip():\n",
    "        question += f'\\n{example[\"input\"]}'\n",
    "\n",
    "    # 构建答案文本\n",
    "    answer = example['summary']\n",
    "\n",
    "    # 对问题和答案文本进行tokenize处理\n",
    "    q_ids = tokenizer.encode(text=question, add_special_tokens=False)\n",
    "    a_ids = tokenizer.encode(text=answer, add_special_tokens=False)\n",
    "\n",
    "    # 如果tokenize后的长度超过最大长度限制，则进行截断\n",
    "    if len(q_ids) > max_input_length - 2:  # 保留空间给gmask和bos标记\n",
    "        q_ids = q_ids[:max_input_length - 2]\n",
    "    if len(a_ids) > max_output_length - 1:  # 保留空间给eos标记\n",
    "        a_ids = a_ids[:max_output_length - 1]\n",
    "\n",
    "    # 构建模型的输入格式\n",
    "    input_ids = tokenizer.build_inputs_with_special_tokens(q_ids, a_ids)\n",
    "    question_length = len(q_ids) + 2  # 加上gmask和bos标记\n",
    "\n",
    "    # 构建标签，对于问题部分的输入使用ignore_label_id进行填充\n",
    "    labels = [ignore_label_id] * question_length + input_ids[question_length:]\n",
    "\n",
    "    return {'input_ids': input_ids, 'labels': labels}\n",
    "\n",
    "# DataCollatorForChatGLM 类\n",
    "class DataCollatorForChatGLM:\n",
    "    \"\"\"\n",
    "    用于处理批量数据的DataCollator，尤其是在使用 ChatGLM 模型时。\n",
    "\n",
    "    该类负责将多个数据样本（tokenized input）合并为一个批量，并在必要时进行填充(padding)。\n",
    "\n",
    "    属性:\n",
    "    pad_token_id (int): 用于填充(padding)的token ID。\n",
    "    max_length (int): 单个批量数据的最大长度限制。\n",
    "    ignore_label_id (int): 在标签中用于填充的ID。\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, pad_token_id: int, max_length: int = 2048, ignore_label_id: int = -100):\n",
    "        \"\"\"\n",
    "        初始化DataCollator。\n",
    "\n",
    "        参数:\n",
    "        pad_token_id (int): 用于填充(padding)的token ID。\n",
    "        max_length (int): 单个批量数据的最大长度限制。\n",
    "        ignore_label_id (int): 在标签中用于填充的ID，默认为-100。\n",
    "        \"\"\"\n",
    "        self.pad_token_id = pad_token_id\n",
    "        self.ignore_label_id = ignore_label_id\n",
    "        self.max_length = max_length\n",
    "\n",
    "    def __call__(self, batch_data: List[Dict[str, List]]) -> Dict[str, torch.Tensor]:\n",
    "        \"\"\"\n",
    "        处理批量数据。\n",
    "\n",
    "        参数:\n",
    "        batch_data (List[Dict[str, List]]): 包含多个样本的字典列表。\n",
    "\n",
    "        返回:\n",
    "        Dict[str, torch.Tensor]: 包含处理后的批量数据的字典。\n",
    "        \"\"\"\n",
    "        # 计算批量中每个样本的长度\n",
    "        len_list = [len(d['input_ids']) for d in batch_data]\n",
    "        batch_max_len = max(len_list)  # 找到最长的样本长度\n",
    "\n",
    "        input_ids, labels = [], []\n",
    "        for len_of_d, d in sorted(zip(len_list, batch_data), key=lambda x: -x[0]):\n",
    "            pad_len = batch_max_len - len_of_d  # 计算需要填充的长度\n",
    "            # 添加填充，并确保数据长度不超过最大长度限制\n",
    "            ids = d['input_ids'] + [self.pad_token_id] * pad_len\n",
    "            label = d['labels'] + [self.ignore_label_id] * pad_len\n",
    "            if batch_max_len > self.max_length:\n",
    "                ids = ids[:self.max_length]\n",
    "                label = label[:self.max_length]\n",
    "            input_ids.append(torch.LongTensor(ids))\n",
    "            labels.append(torch.LongTensor(label))\n",
    "\n",
    "        # 将处理后的数据堆叠成一个tensor\n",
    "        input_ids = torch.stack(input_ids)\n",
    "        labels = torch.stack(labels)\n",
    "\n",
    "        return {'input_ids': input_ids, 'labels': labels}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0e7df9ad-24ee-49b7-888e-0cbcd9d8a291",
   "metadata": {},
   "source": [
    "# 遍历训练数据集合"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "e9d2e480-1367-49ce-8c71-d005475b7072",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "训练数据路径为 data/zhouyi_dataset_handmade.csv\n",
      "导入数据\n",
      "DatasetDict({\n",
      "    train: Dataset({\n",
      "        features: ['content', 'summary'],\n",
      "        num_rows: 8\n",
      "    })\n",
      "})\n",
      "数据处理\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>content</th>\n",
       "      <th>summary</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>山水蒙卦</td>\n",
       "      <td>蒙卦原文：蒙。亨。匪我求童蒙，童蒙求我。初筮告，再三渎，渎则不告。利贞。象曰：山下出泉，蒙。君子以果行育德。白话文解释：蒙卦通泰。不是我求幼稚之人，而是幼稚之人求我。初次占卜被告知，轻慢占卜则不再告知。占卜吉利。《象辞》说：卦象为山下有泉，取法于山泉果敢坚毅，培养品德。《断易天机》解：蒙卦艮上坎下，象征蒙昧，主疑惑不前，多忧愁，凶兆。北宋易学家邵雍解：智慧未开，犹豫不决，需顺师友教导启智。台湾国学大儒傅佩荣解：时运蓄德出世，财运矿业果决吉，家宅君子居吉，婚姻之始，身体驱邪保安。传统解卦：异卦（下坎上艮），山下有险仍前进，为蒙昧，把握时机行动恰时，启蒙通达之象。大象：蒙为昏无所见，宜启蒙。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>水天需卦</td>\n",
       "      <td>需卦原文：需。有孚，光亨，贞吉。利涉大川。象曰：云上于天，需；君子以饮食宴乐。白话文解释：需卦代表俘虏，大吉大利，适宜涉水过河。《象辞》说：上卦为坎，象征云；下卦为乾，象征天。云聚天上，待降雨，君子观此卦，宜宴饮安乐，待时而动。《断易天机》解：需卦坎上乾下，象征踌躇期待，刚强面对险阻，宜等待，涉大川利。北宋易学家邵雍解：遇阻不进，大器晚成，需耐心等待。台湾国学大儒傅佩荣解：时运需耐心等待，财运资本未集，家宅平安，身体调饮食以健康。传统解卦：异卦（下乾上坎），刚逢险，宜稳健，观时待变，必成功。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>地水师卦</td>\n",
       "      <td>师卦原文：师。贞，丈人吉，无咎。象曰：地中有水，师。君子以容民畜众。白话文解释：师卦象征军队指挥，无灾祸。《象辞》说：下卦为坎（水），上卦为坤（地），如大地容纳江河，君子应容纳众人。《断易天机》解：师卦坤上坎下，象征军众，需德高长者统率以吉无咎。北宋易学家邵雍解：忧劳动众，公正无私排难。得卦者应包容他人，努力排除困难。台湾国学大儒傅佩荣解：时运包容他人，财运有财需珍惜，家宅旧亲联姻吉，身体腹胀调气。传统解卦：异卦（下坎上坤），“师”指军队。坎为水险，坤为地顺，寓兵于农，用兵应顺势，故化凶为吉。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>天水讼卦</td>\n",
       "      <td>讼卦原文：讼。有孚，窒惕，中吉，终凶。利见大人，不利涉大川。象曰：天与水违行，讼。君子以做事谋始。白话文解释：讼卦象征虽有利可图但需警惕。事情初吉后凶，利于见贵人，不宜涉水。《象辞》说：上卦为乾（天），下卦为坎（水），天水相隔，事理不合，君子需慎重谋事。《断易天机》解：讼卦乾上坎下，刚遇险，必有争论，多不吉。北宋易学家邵雍解：天高水深，远离不亲，慎谋退守则无凶。得此卦者，身心不安，多争诉，宜修身养性。台湾国学大儒傅佩荣解：时运受阻，财运初谨慎终获利，家宅君子求淑女，身体预防胜于治疗。传统解卦：异卦（下坎上乾），刚健遇险，彼此反对，生争讼，需慎重戒惧。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>坤卦</td>\n",
       "      <td>坤卦原文\\n坤。元，亨，利牝马之贞。君子有攸往，先迷后得主。利西南得朋，东北丧朋。安贞，吉。\\n象曰：地势坤，君子以厚德载物。\\n白话文解释\\n坤卦：大吉大利。占问雌马得到吉兆。君子前去旅行，先迷失路途，后来找到主人，吉利。西南行获得财物，东北行丧失财物。占问定居，得到吉兆。\\n《象辞》说：大地的形势平铺舒展，顺承天道。君子观此卦象，取法于地，以深厚的德行来承担重大的责任。\\n《断易天机》解\\n坤卦坤上坤下，为坤宫本位卦。坤卦为柔顺，为地气舒展之象，具有纯阴之性，先失道而后得主，宜往西南，西南可得到朋友。\\n北宋易学家邵雍解\\n柔顺和静，厚载之功；静守安顺，妄动招损。\\n得此卦者，宜顺从运势，以静制动，不宜独立谋事，顺从他人，一起合作，可成大事。\\n台湾国学大儒傅佩荣解\\n时运：为人厚道，声名远传。\\n财运：满载而归。\\n家宅：家庭安稳；婚嫁大吉。\\n身体：柔软运动。\\n传统解卦\\n这个卦是同卦（下坤上坤）相叠，阴性。象征地（与乾卦相反），顺从天，承载万物，伸展无穷无尽。坤卦以雌马为象征，表明地道生育抚养万物，而又依天顺时，性情温顺。它以“先迷后得”证明“坤”顺从“乾”，依随“乾”，才能把握正确方向，遵循正道，获取吉利。</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/root/miniconda3/envs/lm_ai_learn/lib/python3.10/site-packages/huggingface_hub/file_download.py:945: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "加载模型\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "74cddd8d1bec4c74afc358beed85288d",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Loading checkpoint shards:   0%|          | 0/7 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "You are using an old version of the checkpointing format that is deprecated (We will also silently ignore `gradient_checkpointing_kwargs` in case you passed it).Please update to the new format on your modeling file. To use the new format, you need to completely remove the definition of the method `_set_gradient_checkpointing` in your model.\n",
      "You are using an old version of the checkpointing format that is deprecated (We will also silently ignore `gradient_checkpointing_kwargs` in case you passed it).Please update to the new format on your modeling file. To use the new format, you need to completely remove the definition of the method `_set_gradient_checkpointing` in your model.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "trainable params: 3,899,392 || all params: 6,247,483,392 || trainable%: 0.06241540401681151\n",
      "微调模型\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/root/miniconda3/envs/lm_ai_learn/lib/python3.10/site-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div>\n",
       "      \n",
       "      <progress value='3' max='3' style='width:300px; height:20px; vertical-align: middle;'></progress>\n",
       "      [3/3 00:19, Epoch 3/3]\n",
       "    </div>\n",
       "    <table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       " <tr style=\"text-align: left;\">\n",
       "      <th>Step</th>\n",
       "      <th>Training Loss</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "  </tbody>\n",
       "</table><p>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "完成训练数据 data/zhouyi_dataset_handmade.csv 的模型微调，保存路径为 models/THUDM/chatglm3-6b-1\n",
      "--------------------------------------------------\n",
      "训练数据路径为 data/zhouyi_dataset_20240118_152413.csv\n",
      "导入数据\n",
      "DatasetDict({\n",
      "    train: Dataset({\n",
      "        features: ['content', 'summary'],\n",
      "        num_rows: 160\n",
      "    })\n",
      "})\n",
      "数据处理\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>content</th>\n",
       "      <th>summary</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>{\\n  \"content\": \"屯卦\",\\n  \"summary\": \"在周易中，屯卦代表着大吉大利、吉利的占卜，但不利于出门，有利于建国封侯。其卦象为坎上震下，代表着困难和动而逢险的象征，需要刚毅果敢的态度才能取得吉利。君子观此卦象，取法于云雷，用云的恩泽，雷的威严来治理国事。\",\\n  \"interpretation\": \"屯卦的核心哲学是：万物始生，开始困难；先劳后逸，苦尽甘来。得此卦者，身处困境，宜守不宜进，须多加辛苦努力，排除困难，方可通达，有初难后解之象。在事业上，起初多有不利，要知难而进，小心翼翼，勇往直前，灵活机动，可望获得大的成功，时机到来时一定要抓住，却也不得操之太急，且仍有困难，务必有他人相助，故平时应多施恩惠。在经商方面，创业初期步履艰难，多有挫折。坚定信念最重要，不要为表面现象所迷惑，应积极进取，行动果断，打开出路。若仍无法摆脱困境，则应退守保全，等待机会，再展宏图。而在婚恋中，好事多磨，忠贞纯洁，大胆追求，能够成功，婚姻美满。\"\\n}\\n``在周易中怎样表达教育的概念？</td>\n",
       "      <td>{\\n  \"content\": \"屯卦\",\\n  \"summary\": \"在周易中，屯卦代表着大吉大利、吉利的占卜，但不利于出门，有利于建国封侯。其卦象为坎上震下，代表着困难和动而逢险的象征，需要刚毅果敢的态度才能取得吉利。君子观此卦象，取法于云雷，用云的恩泽，雷的威严来治理国事。\",\\n  \"interpretation\": \"屯卦的核心哲学是：万物始生，开始困难；先劳后逸，苦尽甘来。得此卦者，身处困境，宜守不宜进，须多加辛苦努力，排除困难，方可通达，有初难后解之象。在事业上，起初多有不利，要知难而进，小心翼翼，勇往直前，灵活机动，可望获得大的成功，时机到来时一定要抓住，却也不得操之太急，且仍有困难，务必有他人相助，故平时应多施恩惠。在经商方面，创业初期步履艰难，多有挫折。坚定信念最重要，不要为表面现象所迷惑，应积极进取，行动果断，打开出路。若仍无法摆脱困境，则应退守保全，等待机会，再展宏图。而在婚恋中，好事多磨，忠贞纯洁，大胆追求，能够成功，婚姻美满。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>周易的蒙卦讲述了什么？</td>\n",
       "      <td>在周易中，蒙卦代表着通泰的格局。卦象为艮上坎下，意味着山下有泉，波涌的情形。《象辞》中解释说，君子观此卦象，取法于一往无前的山泉，从而以果敢坚毅的行动来培养自身的品德。\\n\\n蒙卦的核心哲学是：这是一个启蒙的过程，开始时可能面临迷惑和混乱，需要忍耐等待时机，倾听他人意见，通过果敢的行动来扭转局面。\\n\\n蒙卦的传统解卦中指出，初时迷惑不知方向，需要须忍耐待机而动，凡事多听取别人意见，则运可通。因此，在事业、经商、求名、婚恋等方面应当注重实际，接受严格教育和考验，培养果敢坚毅的品质，始能取得成功。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>周易中\"讼卦的解释是什么？</td>\n",
       "      <td>在周易中，讼卦是一个极具深意的卦象。上卦为乾（天），下卦为坎（水），两者相背而行，代表天与水违行的状况，象征着事理乖舛和争讼之象。讼卦中有利可图，但必须警惕戒惧，事情中间吉利，但最终会有凶险。在卜卦时，利于会见贵族王公，但不利于涉水渡河。\\n\\n讼卦的核心哲学是：开始可能顺利，有所收获，但随后会遇到困难和挫折。因此，务必慎之又慎，不得固执已见，避免介入诉讼纠纷的争执之中。退让而不固执，求得化解，安于正理，可免除意外之灾。陷入争讼，即使获胜，最后还得失去，得不偿失。\\n\\n讼卦的经商指引是：和气生财，吃亏是福，切勿追求不义之财。在商业谈判中要坚持公正、公平、互利的原则，尽量避免发生冲突。\\n\\n对于决策，讼卦提醒我们，争强好胜，不安于现状，为改变命运和超越他人而奋斗。但缺乏持之以恒的毅力，容易得罪他人，带来诉讼之灾。因此，接受教训，引以为戒，可功成名就。\\n\\n讼卦所蕴含的智慧是：在面对争端和异见时，要善于退让和求和，坚守正道，谨慎处事，以避免不必要的冲突和损失。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>请解释一下\"讼卦。</td>\n",
       "      <td>在周易中，讼卦是一个极具深意的卦象。上卦为乾（天），下卦为坎（水），两者相背而行，代表天与水违行的状况，象征着事理乖舛和争讼之象。讼卦中有利可图，但必须警惕戒惧，事情中间吉利，但最终会有凶险。在卜卦时，利于会见贵族王公，但不利于涉水渡河。\\n\\n讼卦的核心哲学是：开始可能顺利，有所收获，但随后会遇到困难和挫折。因此，务必慎之又慎，不得固执已见，避免介入诉讼纠纷的争执之中。退让而不固执，求得化解，安于正理，可免除意外之灾。陷入争讼，即使获胜，最后还得失去，得不偿失。\\n\\n讼卦的经商指引是：和气生财，吃亏是福，切勿追求不义之财。在商业谈判中要坚持公正、公平、互利的原则，尽量避免发生冲突。\\n\\n对于决策，讼卦提醒我们，争强好胜，不安于现状，为改变命运和超越他人而奋斗。但缺乏持之以恒的毅力，容易得罪他人，带来诉讼之灾。因此，接受教训，引以为戒，可功成名就。\\n\\n讼卦所蕴含的智慧是：在面对争端和异见时，要善于退让和求和，坚守正道，谨慎处事，以避免不必要的冲突和损失。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>在周易中，需卦象征着什么？</td>\n",
       "      <td>需卦\"由上卦坎（水）和下卦乾（天）组成，上卦为云，下卦为天。上卦坎为险，下卦乾为刚，象征着云浮聚于天上，待时降雨之象。预示抓到俘虏，大吉大利，有利于涉水渡河。君子观此卦象，可以宴饮安乐，待时而动。\\n\\n这个卦象表示踌躇期待，虽然刚强，但前面有险阻，应当等待，涉大川则利。时机尚未成熟，需要耐心等待，不宜冒进。谋事应审时度势，耐心等待，切勿冒险，欲速不达。自信、坚守中正，可化险为夷。情况有利时，仍得居安思危。\\n\\n需卦的核心哲学是谋事应审时度势，耐心等待，切勿冒险，欲速不达。自信、坚守中正，可以化险为夷。需要控制饮食，保持健康的生活方式。在感情和婚姻方面，需要慎重、诚实、和柔克刚。\\n\\n在行动之初，需要极大的耐心，观时待变，创造条件和机会。等待时机成熟后，大器必定晚成。在事业发展中，遇到困难和险阻时，必须十分谨慎，坦然对待小人的中伤。为人处事应谦和、坦率，多有他人相助，促使事业成功。当时机成熟后，则必然一帆风顺。\"\\n\\nsource:\"《易经》</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "加载模型\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "b4c8d1821b03450db7a6cc5eaee23285",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Loading checkpoint shards:   0%|          | 0/7 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "You are using an old version of the checkpointing format that is deprecated (We will also silently ignore `gradient_checkpointing_kwargs` in case you passed it).Please update to the new format on your modeling file. To use the new format, you need to completely remove the definition of the method `_set_gradient_checkpointing` in your model.\n",
      "You are using an old version of the checkpointing format that is deprecated (We will also silently ignore `gradient_checkpointing_kwargs` in case you passed it).Please update to the new format on your modeling file. To use the new format, you need to completely remove the definition of the method `_set_gradient_checkpointing` in your model.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "trainable params: 3,899,392 || all params: 6,247,483,392 || trainable%: 0.06241540401681151\n",
      "微调模型\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div>\n",
       "      \n",
       "      <progress value='60' max='60' style='width:300px; height:20px; vertical-align: middle;'></progress>\n",
       "      [60/60 12:39, Epoch 3/3]\n",
       "    </div>\n",
       "    <table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       " <tr style=\"text-align: left;\">\n",
       "      <th>Step</th>\n",
       "      <th>Training Loss</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>10</td>\n",
       "      <td>3.065400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>20</td>\n",
       "      <td>0.973400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>30</td>\n",
       "      <td>0.096400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>40</td>\n",
       "      <td>0.013400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>50</td>\n",
       "      <td>0.005300</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>60</td>\n",
       "      <td>0.003700</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table><p>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/root/miniconda3/envs/lm_ai_learn/lib/python3.10/site-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n",
      "/root/miniconda3/envs/lm_ai_learn/lib/python3.10/site-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n",
      "/root/miniconda3/envs/lm_ai_learn/lib/python3.10/site-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n",
      "/root/miniconda3/envs/lm_ai_learn/lib/python3.10/site-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n",
      "/root/miniconda3/envs/lm_ai_learn/lib/python3.10/site-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "完成训练数据 data/zhouyi_dataset_20240118_152413.csv 的模型微调，保存路径为 models/THUDM/chatglm3-6b-2\n",
      "--------------------------------------------------\n",
      "训练数据路径为 data/zhouyi_dataset_20240118_163659.csv\n",
      "导入数据\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "d6cce3b5a21d475882954658ce1eb670",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Generating train split: 0 examples [00:00, ? examples/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "DatasetDict({\n",
      "    train: Dataset({\n",
      "        features: ['content', 'summary'],\n",
      "        num_rows: 160\n",
      "    })\n",
      "})\n",
      "数据处理\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>content</th>\n",
       "      <th>summary</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>屯卦在周易中怎样表达教育的概念？</td>\n",
       "      <td>在周易中，屯卦是一个大吉大利的卦象，预示着吉祥和大利。然而，不利于出门，但有利于建国封侯。屯卦由上卦坎（水）下卦震（雷）组成，坎为云，震为雷。预示着云行雷动的卦象。君子观此卦象，取法于云雷，用云的恩泽，雷的威严来治理国事。屯卦象征着开始困难，需要毅力和果敢才能获得吉利。身处困境需要多加辛苦努力，排除困难，方可通达，有初难后解之象。因此，对于事业创业而言，应当小心翼翼，勇往直前，灵活机动，可望获得大的成功。但也需注意到仍有困难存在，务必有他人相助，平时应多施恩惠。对于经商，起初多有挫折，必须坚定信念，积极进取，行动果断，若仍无法摆脱困境，则应退守保全，等待机会，再展宏图。对于婚恋，好事多磨，忠贞纯洁，大胆追求，能够成功。屯卦的核心哲学在于，初难后解，需要毅力和坚忍不拔的毅力和锲而不舍的奋斗精神，但也需得到贤德之人的帮助才能摆脱困境。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>周易的蒙卦讲述了什么？</td>\n",
       "      <td>蒙卦是由艮卦（山）下，坎卦（水）上组成的异卦相叠。它代表着通泰，启蒙的意义。在这里，卜者并非是在向幼稚愚昧的人取求，而是幼稚愚昧的人在向卜者求教。第一次卜筮就得到了神灵的指示。然而，如果轻慢不敬地再三卜筮的话，神灵便不会再示警。总的来说，这是一个吉利的卜问。\\n\\n蒙卦的核心在于山下有泉的形象，寓意着启蒙。君子观此卦象，应当以果敢坚毅的行动来培养自身的品德，像山泉一样果断行动。然而，此卦乃是离宫四世卦，它代表着回还往复、疑惑不前、多忧愁过失，因而属于凶卦。\\n\\n蒙卦在个人发展、事业经商、求名婚恋等方面的解释不一。在事业方面，表示事业初建，具有启蒙和通达之象，需要勇敢坚毅的行动；而在经商方面，需要务必小心谨慎，树立高尚的商业道德，不可急功近利；求名方面，需要接受良好的基础教育，陶冶情操。整体而言，此卦提示须忍耐待机而动，听取别人意见，方能通达运势。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>周易中讼卦的解释是什么？</td>\n",
       "      <td>在周易中，讼卦是一个充满警示的卦象。它由上卦乾（天）和下卦坎（水）组成，代表着天与水背道而驰，形成争讼的局面。虽然事情开始时有利可图，但必须警惕戒惧，因为中间虽然吉利，但最终会带来凶险。对于涉及大川，涉水渡河的行动不利。因此，君子观此卦象，应当慎之又慎，杜绝争讼之事，并在谋事之初谨慎行事。讼卦的核心哲学是要避免争讼，退而让人，求得化解，安于正理，方可避免意外之灾。在事业上，务必避免介入诉讼纠纷的争执之中，与其这样，不如退而让人。即使最终获胜，也难免得失不均。经商方面，要坚持公正、公平、互利的原则，避免冲突，这样会有好结果。而对于求名、婚恋和决策，也都需要慎重行事，避免盲目追求，退让让人，可助事业、婚姻和决策的发展。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>请解释一下讼卦。</td>\n",
       "      <td>在周易中，讼卦是一个充满警示的卦象。它由上卦乾（天）和下卦坎（水）组成，代表着天与水背道而驰，形成争讼的局面。虽然事情开始时有利可图，但必须警惕戒惧，因为中间虽然吉利，但最终会带来凶险。对于涉及大川，涉水渡河的行动不利。因此，君子观此卦象，应当慎之又慎，杜绝争讼之事，并在谋事之初谨慎行事。讼卦的核心哲学是要避免争讼，退而让人，求得化解，安于正理，方可避免意外之灾。在事业上，务必避免介入诉讼纠纷的争执之中，与其这样，不如退而让人。即使最终获胜，也难免得失不均。经商方面，要坚持公正、公平、互利的原则，避免冲突，这样会有好结果。而对于求名、婚恋和决策，也都需要慎重行事，避免盲目追求，退让让人，可助事业、婚姻和决策的发展。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>在周易中，需卦象征着什么？</td>\n",
       "      <td>需卦是一个占卜卦象，在周易卦象中，它由下卦乾和上卦坎组成。坎象征着云，乾象征着天，云聚于天，形成了等待时机的卦象。这是一个大吉大利的卜问，特别适合涉水渡河。根据《象辞》，君子观此卦象，可以宴饮安乐，待时而动。\\n\\n需卦的核心哲学是：稳扎稳打，不可冒失行动，观时待变，耐心等待。在事业上，需要审时度势，守中正，不可急进，自信充满，可化险为夷。在经商中，必须充满耐心，创造条件和机会，行事光明磊落，等到时机成熟后必然一帆风顺。在感情方面，亦需慎重，培养感情，以诚实、热情相待，时机成熟后可以有良好的结果。\\n\\n需卦紧随坤宫游魂卦，预示着踌躇和期待。得此卦者，需要时机尚未成熟，需要耐心等待，急进反会见凶险。</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/root/miniconda3/envs/lm_ai_learn/lib/python3.10/site-packages/huggingface_hub/file_download.py:945: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "758b309cb085465f89af4a4413a3e0df",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Map:   0%|          | 0/160 [00:00<?, ? examples/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "ed9ddc37e424490986a0de41adc0985e",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Flattening the indices:   0%|          | 0/160 [00:00<?, ? examples/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "加载模型\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "a86fa0629064471fb9bc257779a27e0b",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Loading checkpoint shards:   0%|          | 0/7 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "You are using an old version of the checkpointing format that is deprecated (We will also silently ignore `gradient_checkpointing_kwargs` in case you passed it).Please update to the new format on your modeling file. To use the new format, you need to completely remove the definition of the method `_set_gradient_checkpointing` in your model.\n",
      "You are using an old version of the checkpointing format that is deprecated (We will also silently ignore `gradient_checkpointing_kwargs` in case you passed it).Please update to the new format on your modeling file. To use the new format, you need to completely remove the definition of the method `_set_gradient_checkpointing` in your model.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "trainable params: 3,899,392 || all params: 6,247,483,392 || trainable%: 0.06241540401681151\n",
      "微调模型\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/root/miniconda3/envs/lm_ai_learn/lib/python3.10/site-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div>\n",
       "      \n",
       "      <progress value='60' max='60' style='width:300px; height:20px; vertical-align: middle;'></progress>\n",
       "      [60/60 06:38, Epoch 3/3]\n",
       "    </div>\n",
       "    <table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       " <tr style=\"text-align: left;\">\n",
       "      <th>Step</th>\n",
       "      <th>Training Loss</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>10</td>\n",
       "      <td>3.812500</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>20</td>\n",
       "      <td>1.458600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>30</td>\n",
       "      <td>0.158400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>40</td>\n",
       "      <td>0.016400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>50</td>\n",
       "      <td>0.005900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>60</td>\n",
       "      <td>0.004200</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table><p>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/root/miniconda3/envs/lm_ai_learn/lib/python3.10/site-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n",
      "/root/miniconda3/envs/lm_ai_learn/lib/python3.10/site-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n",
      "/root/miniconda3/envs/lm_ai_learn/lib/python3.10/site-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n",
      "/root/miniconda3/envs/lm_ai_learn/lib/python3.10/site-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n",
      "/root/miniconda3/envs/lm_ai_learn/lib/python3.10/site-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "完成训练数据 data/zhouyi_dataset_20240118_163659.csv 的模型微调，保存路径为 models/THUDM/chatglm3-6b-3\n",
      "--------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "# 定义训练数据序号\n",
    "data_sn=0\n",
    "for train_data_path in train_data_pathes:\n",
    "    data_sn=data_sn+1\n",
    "    print(\"训练数据路径为\",train_data_path)\n",
    "\n",
    "    print(\"导入数据\")\n",
    "    dataset = load_dataset(\"csv\", data_files=train_data_path)\n",
    "    print(dataset)\n",
    "    \n",
    "    print(\"数据处理\")\n",
    "    show_random_elements(dataset[\"train\"], num_examples=5)\n",
    "    \n",
    "    tokenizer = AutoTokenizer.from_pretrained(model_name_or_path,trust_remote_code=True,revision='b098244')\n",
    "    column_names = dataset['train'].column_names\n",
    "    tokenized_dataset = dataset['train'].map(\n",
    "        lambda example: tokenize_func(example, tokenizer),\n",
    "        batched=False, \n",
    "        remove_columns=column_names\n",
    "    )\n",
    "\n",
    "    tokenized_dataset = tokenized_dataset.shuffle(seed=seed)\n",
    "    tokenized_dataset = tokenized_dataset.flatten_indices()\n",
    "\n",
    "    # 准备数据整理器\n",
    "    data_collator = DataCollatorForChatGLM(pad_token_id=tokenizer.pad_token_id)\n",
    "    # 加载模型\n",
    "    print(\"加载模型\")\n",
    "    _compute_dtype_map = {\n",
    "        'fp32': torch.float32,\n",
    "        'fp16': torch.float16,\n",
    "        'bf16': torch.bfloat16\n",
    "    }\n",
    "\n",
    "    # QLoRA 量化配置\n",
    "    q_config = BitsAndBytesConfig(load_in_4bit=True,\n",
    "                                  bnb_4bit_quant_type='nf4',\n",
    "                                  bnb_4bit_use_double_quant=True,\n",
    "                                  bnb_4bit_compute_dtype=_compute_dtype_map['bf16'])\n",
    "    # 加载量化后模型\n",
    "    model = AutoModel.from_pretrained(model_name_or_path,\n",
    "                                      quantization_config=q_config,\n",
    "                                      device_map='auto',\n",
    "                                      trust_remote_code=True,\n",
    "                                      revision='b098244')\n",
    "\n",
    "    model.supports_gradient_checkpointing = True  \n",
    "    model.gradient_checkpointing_enable()\n",
    "    model.enable_input_require_grads()\n",
    "    model.config.use_cache = False  # silence the warnings. Please re-enable for inference!\n",
    "    \n",
    "    kbit_model = prepare_model_for_kbit_training(model)\n",
    "    target_modules = TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING['chatglm']\n",
    "\n",
    "    lora_config = LoraConfig(\n",
    "        target_modules=target_modules,\n",
    "        r=lora_rank,\n",
    "        lora_alpha=lora_alpha,\n",
    "        lora_dropout=lora_dropout,\n",
    "        bias='none',\n",
    "        inference_mode=False,\n",
    "        task_type=TaskType.CAUSAL_LM\n",
    "    )\n",
    "    qlora_model = get_peft_model(kbit_model, lora_config)\n",
    "    qlora_model.print_trainable_parameters()\n",
    "\n",
    "    # 微调模型\n",
    "    print(\"微调模型\")\n",
    "    \n",
    "    timestamp = datetime.datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
    "    train_epochs = 3\n",
    "    output_dir = f\"models/{model_name_or_path}-{data_sn}\"\n",
    "\n",
    "    \n",
    "    #配置训练参数\n",
    "    training_args = TrainingArguments(\n",
    "        output_dir=output_dir,                            # 输出目录\n",
    "        per_device_train_batch_size=8,                     # 每个设备的训练批量大小\n",
    "        gradient_accumulation_steps=1,                     # 梯度累积步数\n",
    "        learning_rate=1e-3,                                # 学习率\n",
    "        num_train_epochs=train_epochs,                     # 训练轮数\n",
    "        lr_scheduler_type=\"linear\",                        # 学习率调度器类型\n",
    "        warmup_ratio=0.1,                                  # 预热比例\n",
    "        logging_steps=10,                                 # 日志记录步数\n",
    "        save_strategy=\"steps\",                             # 模型保存策略\n",
    "        save_steps=10,                                    # 模型保存步数\n",
    "        optim=\"adamw_torch\",                               # 优化器类型\n",
    "        fp16=True,                                        # 是否使用混合精度训练\n",
    "    )\n",
    "    trainer = Trainer(\n",
    "        model=qlora_model,\n",
    "        args=training_args,\n",
    "        train_dataset=tokenized_dataset,\n",
    "        data_collator=data_collator\n",
    "    )\n",
    "    trainer.train()\n",
    "    trainer.model.save_pretrained(output_dir)\n",
    "    print(\"完成训练数据\",train_data_path,\"的模型微调，保存路径为\",output_dir)\n",
    "    torch.cuda.empty_cache()\n",
    "    print(\"-\"*50)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "27121828-4980-420c-90e7-467ffaa4c388",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "完成三个训练数据的微调\n"
     ]
    }
   ],
   "source": [
    "print(\"完成三个训练数据的微调\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2a69e634-3589-4389-a6fa-7c086c7caa3e",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.18"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
